/*
 * @Author: lokei
 * @Date: 2022-09-06 15:58:20
 * @LastEditors: lokei
 * @LastEditTime: 2022-09-08 22:37:12
 * @Description: 
 */
package cn.lokei.coap.handler;

import java.lang.reflect.Method;
import java.util.Set;

import cn.lokei.coap.annotation.CoapGetMapping;
import cn.lokei.coap.annotation.CoapResource;
import cn.lokei.coap.common.ClassUtil;
import cn.lokei.coap.entity.Option;
import cn.lokei.coap.entity.Packet;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class UdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
        Packet coap_packet = new Packet();
        int b1 = Byte.toUnsignedInt(packet.content().getByte(0));
        coap_packet.setVar(b1 >>> 6);
        coap_packet.setT((b1 % 64) >>> 4);
        coap_packet.setTKL(b1 % 16);
        coap_packet.setCode(Byte.toUnsignedInt(packet.content().getByte(1)));
        int b3 = Byte.toUnsignedInt(packet.content().getByte(2));
        int b4 = Byte.toUnsignedInt(packet.content().getByte(3));
        coap_packet.setMessageID((b3 << 8) + b4);
        if(coap_packet.getTKL() > 0) {
            packet.content().toString(4, coap_packet.getTKL(), CharsetUtil.UTF_8);
        }
        int offset = 4 + coap_packet.getTKL();
        while(true) {
            int b = Byte.toUnsignedInt(packet.content().getByte(offset));
            if(b == 0) {
                break;
            } else if(b < 240) {
                int delta = b >> 4;
                if (delta == 13) {
                    delta += Byte.toUnsignedInt(packet.content().getByte(offset + 1));
                    offset += 1;
                } else if (delta == 14) {
                    delta += (Byte.toUnsignedInt(packet.content().getByte(offset + 1)) << 8) + Byte.toUnsignedInt(packet.content().getByte(offset + 2));
                    offset += 2;
                }
                offset += 1;
                int length = b % 16;
                if(length == 13) {
                    length += Byte.toUnsignedInt(packet.content().getByte(offset));
                    offset += 1;
                } else if (length == 14) {
                    length += (Byte.toUnsignedInt(packet.content().getByte(offset)) << 8) + Byte.toUnsignedInt(packet.content().getByte(offset + 1));
                    offset += 2;
                }
                String value = packet.content().toString(offset, length, CharsetUtil.UTF_8);
                Option option = new Option(delta, value);
                coap_packet.addOption(option);
                offset += length;
            } else if(b >= 240 && b < 255 ) {
                log.info("package format error!");
                break;
            } else {
                // parse payload
                break;
            }
        }

        Set<Class<?>> classes = ClassUtil.getClasses("cn.lokei");
        for (Class<?> class1 : classes) {
            if(class1.getAnnotation(CoapResource.class) != null) {
                Method[] methods = class1.getMethods();
                for (Method method : methods) {
                    if(method.getAnnotation(CoapGetMapping.class) != null) {
                        if(method.getAnnotation(CoapGetMapping.class).value().equals(coap_packet.getOptions().get(0).getValue())) {
                            method.invoke(class1.getDeclaredConstructor().newInstance(), ctx, packet);
                        }
                    }
                }
            }
        }
    }
    
}
